#pragma once
#include <lcir/IR/Context.h>
#include <lcir/IR/Dialect.h>
#include <lcir/IR/LambdaDialect.h>
#include <lcir/Utils/Utils.h>

#include <initializer_list>
#include <iostream>
#include <sstream>
#include <string_view>
#include <type_traits>
#include <vector>
namespace lcir {
/// Type分为两种
/// 1. 基本type，基本type需要提供无参构造
/// 2. 复杂type，复杂type拥有n个指向type的成员指针
///    复杂type必须提供无参构造函数，以及构造n个指向type的成员指针的构造函数
///    成员Type在初始化后不能改变
/// 判断两个type类型是否相等分为普通判断和严格判断
/// 普通判断不会检查complexType的成员指针是否全部相等，严格判断会
/// isa是普通判断
class Type {
  const std::string name;
  const Dialect *dialect{nullptr};
  const Type *instance{nullptr};

public:
  Type() = delete;
  Type(std::string_view name, const Dialect *dialect, const Type *instance)
      : name(name), dialect(dialect), instance(instance) {}
  const Dialect *getDialect() const { return dialect; }
  std::string getName() const {
    std::stringstream ss;
    ss << dialect->getName() << "." << name;
    return ss.str();
  }
  virtual std::string toString() const { return getName(); }

  // 获取这个type的instance
  const Type *getInstance() const { return instance; }

  template <class OtherType> bool isa() const {
    return this->getInstance() == OtherType::getLooseType();
  }

  template <class OtherType> OtherType *dyn_cast() {
    if (!this->isa<OtherType>())
      return nullptr;
    return static_cast<OtherType *>(this);
  }

  template <class OtherType> const OtherType *dyn_cast() const {
    if (!this->isa<OtherType>())
      return nullptr;
    return static_cast<const OtherType *>(this);
  }
  Type(const Type &) = delete;
  Type(const Type &&) = delete;
  Type &operator=(const Type &) = delete;
  Type &operator=(const Type &&) = delete;
};

// TypeCRTP
template <class DerivedTy, class DialectTy> class TypeCRTP : public Type {
public:
  TypeCRTP(std::string_view name, const Type *instance)
      : Type(name, DialectTy::get(), instance) {}
};

// 基本类型
template <class DerivedTy, class DialectTy>
class BasicTypeCRTP : public TypeCRTP<DerivedTy, DialectTy> {
  friend class GlobalContext;
  // const Type *getInstance() const override { return get(); }

public:
  using basicType = BasicTypeCRTP<DerivedTy, DialectTy>;
  BasicTypeCRTP(std::string_view name)
      : TypeCRTP<DerivedTy, DialectTy>(name, this) {}

  static DerivedTy *get() {
    auto *ctx = GlobalContext::get();
    return ctx->getType<DerivedTy>();
  }
  static DerivedTy *getLooseType() { return get(); }
};

// 复杂类型
template <class DerivedTy, class DialectTy>
class ComplexTypeBase : public TypeCRTP<DerivedTy, DialectTy> {
  friend class GlobalContext;
  // const Type *getInstance() const override { return getLooseType(); }

public:
  using complexType = ComplexTypeBase<DerivedTy, DialectTy>;
  ComplexTypeBase(std::string_view name)
      : TypeCRTP<DerivedTy, DialectTy>(name, getLooseType()) {}
  static const Type *getLooseType() {
    auto *ctx = GlobalContext::get();
    return ctx->getComplexLooseType<DerivedTy>();
  }
};

// 可变复杂类型
template <class DerivedTy, class DialectTy>
class VarComplexTypeCRTP : public ComplexTypeBase<DerivedTy, DialectTy> {
  friend class GlobalContext;

public:
  VarComplexTypeCRTP(std::string_view name)
      : ComplexTypeBase<DerivedTy, DialectTy>(name) {}
  template <class... Args>
  static DerivedTy *get(std::initializer_list<Type *> list, Args... args) {
    std::vector<Type *> vec = list;
    return get(vec, args...);
  }
  template <class... Args>
  static DerivedTy *get(const std::vector<Type *> &vec, Args... args) {
    auto *ctx = GlobalContext::get();
    return ctx->getType<DerivedTy>(vec, args...);
  }
  static DerivedTy *get() {
    auto *ctx = GlobalContext::get();
    return ctx->getType<DerivedTy>();
  }
};

// 普通复杂类型
template <class DerivedTy, class DialectTy>
class ComplexTypeCRTP : public ComplexTypeBase<DerivedTy, DialectTy> {
  friend class GlobalContext;

public:
  using complexType = ComplexTypeCRTP<DerivedTy, DialectTy>;
  ComplexTypeCRTP(std::string_view name)
      : ComplexTypeBase<DerivedTy, DialectTy>(name) {}
  template <class... Args> static DerivedTy *get(Args *...args) {
    auto *ctx = GlobalContext::get();
    return ctx->getType<DerivedTy>(args...);
  }
};

} // namespace lcir